
require "responderchain"

# Classes for our game objects:

# Mixin for Widgets that have children.
# Mixing in event forwarder enables a widget to properly forward events to its children widgets.
module EventForwarder
	#Filter events to @children
	def receive_event(event)
		@children.reverse_each do |child|
			if child.responds_to_event?(event)
				child.receive_event(event)
				break
			end
		end
	end
end

# Mixin for Widgets that don't have children
# This isn't entirely true. Widgets like button are composed of more basic widgets. Mixin EventSink if your widget isn't some type of panel
module EventSink
	#Turn the event into a method name and call it on self
	def  receive_event(event)
		#get the event class name
		name=event.class.to_s.split("::")
		name=name[name.size-1]
		#turn class naming convention into method naming convertion
		name=name.gsub(/[A-Z]/, "_\\0")
		name.downcase!
		#prepend leading "on"
		name="on"+name
		#remove trailing _event
		@name=name.gsub(/_event/, "")
		#call the generated method name
		self.send(:"#{@name}").call(event)
	end
end


# Parent class for all GUI elements. Mixes in Sprite so that all Widgets are able to draw themselves to the screen.
# Every class that inheirits from Widget must mix in EventForwarder or EventSink to ensure that events are properly handled
class Widget
	
	class Delegate
		def initialize()
			@callbacks=[]
		end
		def add_handler(&handler)
			@callbacks<<handler
			#puts "delegate#add_handler callbacks: #{@callbacks.size}"
		end
		def remove_handler(handler)
			@callbacks.delete(handler)
		end
		def call(*args)
			@callbacks.each {|callback| 
				#puts "calling callback : #{callback.class.to_s}"
				callback.call(*args)
			}
		end
	end
		
	
	#constants for event handeling
	#OnClick="on_click"
	#OnMouseDown="on_mouse_down"
	#OnMouseUp="on_mouse_up"
	#OnMouseEnter="on_mouse_enter"
	#OnMouseExit="on_mouse_exit"
	#OnKeyDown="on_key_down"
	#OnKeyUp="on_key_up"
	
	attr_accessor :rect,  :image
	include Rubygame::Sprites::Sprite

	#These are needed for magical dynamic method binding
	def metaclass; class << self; self; end; end
   	def meta_eval &blk
		metaclass.instance_eval &blk; 
	end
	def meta_def (name, &blk)
		meta_eval { define_method(name, &blk) }
   	end

	# Create a new Widget
	# size:: an array of 2 integers [x,y]
	# callbacks:: a hash of Symbol->lambda.
	def initialize(size, callbacks={}, parent=nil)
		super()
		@visible=true
		@image=Rubygame::Surface.new(size, 0, Rubygame::HWSURFACE )
		@rect=Rubygame::Rect.new(0,0,*size)
		#@callbacks = callbacks
		@parent=parent
		@clicking=false
		@focus=false
		
		#instatiate all event delegates
		[:on_mouse_up, :on_mouse_down, :on_click, :on_mouse_motion, :on_mouse_drag, :on_key_up, 
		:on_key_down, :on_mouse_enter, :on_mouse_exit].each do |symbol|
			delegate = Delegate.new
			#define events as methods
			meta_def(symbol){delegate}
		end
		
		#link given callbacks to symbols
		 callbacks.each do |symbol, callback|
			delegate=send(symbol)#
			delegate.add_handler &callback
		end
		
		self.on_mouse_down.add_handler {self.set_focus(true)}
		
		#puts "widget initialized. rect: #{rect.inspect}"
	end

	def method_missing(symbol, *args)
		puts "method missing #{self.class.to_s}##{symbol.to_s}"
		#puts caller(0)
	end

	def responds_to_event?(event)
		if event.instance_variables.include?("@pos")
			@rect.collide_point?(*event.pos)
		else
			false
		end
	end

	def remove_from_parent
		@parent.remove(self)
	end
	def set_position(position)
		@rect.topleft = position
		#puts "Widget#set_position [#{position[0]},#{position[1]}]"
	end
	def move(direction)
		x=@rect.x + direction[0]
		y=@rect.y + direction[1]
		@rect.topleft=Array[x,y]
		
		#puts "Widget#move  new position: [#{@rect.topleft[0]},#{@rect.topleft[1]}]"
	end
	def has_focus?
		@focus
	end
	# Calling this has undefined results. It is best to let the system manage focus
	def set_focus(focus)
		@focus=focus
		#sent this object to the Window as the new focused widget
		nil.set_focused_widget(self)
	end	
	# Returns true if visible and false if invisible
	def is_visible?
		@visible
	end
	# Sets the widgets visible state.
	# visible:: true if the widget is to be drawn, false if the widget is not to be drawn
	def set_visible(visible)
		@visible=visible
	end
	
	# Draw the Widget to the screen using the magic of Sprites
	def draw(screen)
		if @visible
			super(screen)
		end
	end
	def update
		#TODO anything to go here?
	end
end

#Hold collection of children
#Children widgets must be contained within the bounds of their parent
class Panel < Widget
	include EventForwarder
	# Create a new panel.
	# If size is given, it will be used. If not given size will be infered
	def initialize(size=nil, background=nil, callbacks=nil)
		super(size, callbacks)
		@children=[]
		@child_sprites = Rubygame::Sprites::Group.new()
		if	background.is_a? Array
			if size===[0,0]
				#TODO change to occupy the maximum space the parent will allow
				@rect=Rubygame::Rect.new(0,0,0,0)
			else
				@rect=Rubygame::Rect.new(0,0,*size)
			end
			image = Rubygame::Surface.new(@rect)
			image.fill(background)
		elsif background.is_a? String
			image=Rubygame::Surface.load_image(background)
			if rect.nil?
				@rect=Rubygame::Rect.new(0,0,*@image.size)
			else
				@rect=Rubygame::Rect.new(0,0,*size)
			end
		else
			#puts "background not an Array or String. Using none."
			@rect=Rubygame::Rect.new(0,0,*size)
		end
			image.blit(@image, [0,0])
	end

	def responds_to_event?(event)
		if event.instance_variables.include?("@pos")
			self.rect.collide_point?(*event.pos)
		elsif event.instance_variables.include?("@key")
			self.has_focus?
		else
			false
		end
	end
	# Reposition a panel in absolute coordinate space.
	# Children widgets will be moved relative to the parent panel.
	def set_position(position)
		dx=position[0]-@rect.x
		dy=position[1]-@rect.y
		super
		@children.each do |child|
			child.move([dx,dy])
		end
	end
	# Reposition a panel in relative coordinate space.
	# Children widgets will be moved relative to the parent panel.
	def move(direction)
		x=@rect.x + direction[0]
		y=@rect.y + direction[1]
		@rect.topleft=[x,y]
		@children.each do |child|
			child.move(direction)
		end
	end
	# Add a widget in coordinate space relative to the parent panel.
	def add_child(widget, position=[0,0])
		widget.set_position([position[0]+@rect.left, position[1]+@rect.top])
		@children<<widget
		@child_sprites.push(widget)
		#puts "added widget at [#{position[0]},#{position[1]}]"
	end
	# Remove all widgets from the parent panel.
	def clear
		@children.clear
		@child_sprites.clear
	end
	# Remove a widget from the parent panel.
	def remove(widget)
		@children.delete(widget)
		@child_sprites.delete(widget)
	end
	# Get an array of children stored in this panel.
	def get_children
		@children
	end
	# Draw this panel and all of its children to the screen.
	def draw(screen)
		@child_sprites.draw(screen)
	end
	# Update this panel and all of its children.
	def update
		super
		@child_sprites.update
	end
end

# Panel that lays children out horizontally
class HorizontalPanel < Panel
	alias panel_add_child add_child
	# Create a new HorizontalPanel
	def initialize(padding=10, callbacks={})
		@padding=padding
		super([0,0])
	end
	# Add a widget to this panel. The Widget will be automatically positioned after the last Widget added.
	def add_child(widget)
		position=0
		if @children.empty?
			position=0
		else
			position=@children.last.rect.x+@children.last.rect.w+@padding-@rect.y
		end
		panel_add_child(widget, [position, @rect.y])

		#resize this widget
		@rect.w=position+widget.rect.w
		@rect.h=[@rect.h,widget.rect.h].max
		#puts "horizontal panel size: [#{@rect.size[0]},#{@rect.size[1]}]"
	end
	#can be an expensive operation.
	def insert_child(widget, index)
		new_children=@children.clone
		new_children[index]=widget
		
		clear
		
		new_children.each do |widget| 
			if widget.nil?
				puts "nil widget in insert_child"
				widget=Widget.new([0,0])
			else
				widget.set_position([0,0])
			end
			add_child(widget)
		end
	end
	def [](i)
		@children[i]
	end
	def []=(index, widget)
		insert_child(widget, index)
	end
	def index(widget)
		@children.index(widget)
	end
end
# Panel that lays children out vertically
class VerticalPanel < Panel
	alias panel_add_child add_child
	def initialize(padding=10, callbacks={})
		@padding=padding
		super([0,0])
	end
	# Add a widget to this panel. The Widget added will be automatically positioned below the last Widget added.
	def add_child(widget)
		position=0
		if @children.empty?
			position=0
		else
			position=@children.last.rect.y+@children.last.rect.h+@padding-@rect.y
		end
		panel_add_child(widget, [@rect.x, position])
		
		#resize this widget
		@rect.w=[@rect.w,widget.rect.w].max
		@rect.h=position+widget.rect.h
	end
	#can be an expensive operation.
	def insert_child(widget, index)
		new_children=@children.clone
		new_children[index]=widget
		
		clear
		
		new_children.each do |widget| 
			if widget.nil?
				puts "nil widget in insert_child"
				widget=Widget.new([0,0])
			else
				widget.set_position([0,0])
			end
			add_child(widget)
		end
	end
	def [](i)
		@children[i]
	end
	def []=(index, widget)
		insert_child(widget, index)
	end
	def index(widget)
		@children.index(widget)
	end
end
# Image displays an image on the screen
class Image < Widget
	include EventSink
	def initialize(path, size=nil)
		@path=path
		image=Rubygame::Surface.load_image(path)
		super(image.size, {})
		image.set_colorkey([0,0,0])
		@image=image
	end
	def clone
		Image.new(@path)
	end
	def update
	
	end
end
# Displays widgets in a grid, positionable by x,y cell index. The table automatically expands to fit new children.
#TODO implement this widget
class TablePanel < Panel
	alias panel_add_child add_child
	def initialize(horizontal_padding=10, vertical_padding=10)
		#TODO implement
		@horizontal_padding=horizontal_padding
		@vertical_padding=vertical_padding
		
	end
	def add_child(widget, position)
		#TODO implement
		
	end
	
end
# Label displays text to the screen
class Label < Widget
	include EventSink
	def initialize(text, theme=ThemeManager.instance.default_theme.label, size=nil)
		if text.strip===""
			text=" "
		end
		@text=text
		@color=theme.color
		@font=theme.font
		rendered_text=nil
		rect=nil
		puts "#{@font.size_text(@text).class.to_s}"
		if size.nil?
			rendered_text = @font.render(@text, true, @color)
			rect = Rubygame::Rect.new(0,0,*rendered_text.size)
			puts "size is nil, rendered_text: #{rendered_text.inspect}"
		#check if the text needs to be wrapped
		elsif @font.size_text(@text)[0]>size
			#TODO implement text wrapping algorithm
			rendered_text = @font.render(@text, true, @color)
			rect = Rubygame::Rect.new(0,0,*rendered_text.size)
			puts "textwrapping, rendered_text: #{rendered_text.inspect}, #{rect.inspect}"
		else
			rendered_text = @font.render(@text, true, @color)
			rect = Rubygame::Rect.new(0,0,size, rendered_text.size[1])
			
			puts "size but no textwrapping, rendered_text: #{rendered_text.inspect}"
		end
		
		puts "rect: #{rect.w},#{rect.h}"
		super(rect.size)
		@image=rendered_text
	end

	def get_text
		@text
	end
	# Changes the text. The next time this Label is drawn the new text will be shown.
	def set_text(text)
		#render a new text object and readjust the widget size to match
		@text=text
		rendered_text = @font.render(@text, true, @color)
		@image=rendered_text
	end
end
# Text box displays text and allows for editing
class TextBox < Widget
	include EventSink
	def initialize(size=100, text="", theme=ThemeManager.instance.default_theme.textbox)
		@size=size
		text=text+"|"
		@label=Label.new(text, theme, size)
		@frame=Frame.new(@label, theme)
		@cursor_pos=text.length
		super(@label.rect.size, {
			:on_key_down=>lambda{|*args|
			#accept arrow keys to move cursor position
				puts "TextBox got KeyDownEvent"
				text=@label.get_text
				case args[0].key
					when Rubygame::K_BACKSPACE
						if text.size>1
							text[@cursor_pos-2]=""
							@cursor_pos-=1
						end
					when Rubygame::K_DELETE
						if text.size>1and @cursor_pos!=text.size
							text[@cursor_pos]=""
						end
					when Rubygame::K_LEFT
						if @cursor_pos>1
							text[@cursor_pos-1]=""
							@cursor_pos-=1
							text=text.insert(@cursor_pos-1, "|")
						end
					when Rubygame::K_RIGHT
						if @cursor_pos<text.size
							text[@cursor_pos-1]=""
							@cursor_pos+=1
							text=text.insert(@cursor_pos-1, "|")
						end
					when Rubygame::K_SPACE..Rubygame::K_Z
						if text.size<=@size
							#text[@cursor_pos]=""
							text=text.insert(@cursor_pos-1, args[0].string)
							@cursor_pos+=1
						end
				end
				@label.set_text(text)
			}
			}
		)
		@rect.size=@frame.rect.size
		#puts "textbox size: [#{@rect.size[0]},#{@rect.size[1]}]"
	end
	def set_position(position)
		dx=position[0]-@rect.x
		dy=position[1]-@rect.y
		super
		@frame.move([dx,dy])
	end
	def move(direction)
		x=@rect.x + direction[0]
		y=@rect.y + direction[1]
		@rect.topleft=[x,y]
		@frame.move(direction)
	end
	def draw(screen)
		#TODO draw blinking cursor
		@frame.draw(screen)
	end
	def update
		#TODO update cursor blink!
	end
	def get_text
		text=@label.get_text.clone
		text[@cursor_pos-1]=""
		text
	end
end
	
# Button displays a background image and text and allows for clicking
class Button < Widget
	include EventSink
	def initialize(widget, callbacks={}, theme=ThemeManager.instance.default_theme.button, frame=nil)
		if widget.is_a? String
			@widget=Label.new(widget, theme)
		else
			@widget=widget
		end
		if frame.nil?
			@frame=Frame.new(@widget, theme)
		else
			@frame=frame
		end
		
		size=@frame.rect.size
		super(size, callbacks)
		#blit the background onto the sprite image
		#@background.image.blit(@image, [0,0])
		#blit the text onto the background image
		#@label.image.blit(@image, [0,0])
	end
	def set_position(position)
		dx=position[0]-@rect.x
		dy=position[1]-@rect.y
		super
		@frame.move([dx,dy])
	end
	def move(direction)
		x=@rect.x + direction[0]
		y=@rect.y + direction[1]
		@rect.topleft=[x,y]
		@frame.move(direction)
	end
	def draw(screen)
		@frame.draw(screen)
	end
	def update
	
	end
end
# Frame encloses a widget with a dynamically sized border
class Frame <Panel
	Top=0
	
	def initialize(center_widget, theme, sides=[:top, :left, :right, :bottom])
		super([0,0])
		#create 4 corners and 4 sides and middle
		#position the widget in the middle so that
		#tl   t   tr
		# l   w   r
		#bl  b  br
		#position center widget on top of the middle widget
		@tl=theme.topleft
		@t=theme.top
		@tr=theme.topright
		@l=theme.left
		@m=theme.middle
		@r=theme.right
		@bl=theme.bottomleft
		@b=theme.bottom
		@br=theme.bottomright
		
		x0=@rect.x
		y0=@rect.y
		x1=x0+[@tl.rect.w, @l.rect.w, @bl.rect.w].max
		y1=y0+[@tl.rect.h, @t.rect.h, @tr.rect.h].max
		x2=x1+center_widget.rect.w
		y2=y1+center_widget.rect.h
		#puts "center widget [#{center_widget.rect.w}, #{center_widget.rect.h}]"
		x3=x2+[@tr.rect.w, @r.rect.w, @br.rect.w].max
		y3=y2+[@bl.rect.h, @b.rect.h, @br.rect.h].max
		
		
		@t.rect.w=x2-x1
		@b.rect.w=x2-x1
		@l.rect.h=y2-y1
		@r.rect.h=y2-y1
		@m.rect.w=center_widget.rect.w
		@m.rect.h=center_widget.rect.h
		
		#expand some of the peices by 1. No idea why this needs to be done.
		#TODO findout why some peices are too small.
		@tl.image=@tl.image.zoom_to(*@tl.rect.size)
		@t.image=@t.image.zoom_to(@t.rect.w+1, @t.rect.h)
		@tr.image=@tr.image.zoom_to(*@tr.rect.size)
		@l.image=@l.image.zoom_to(@l.rect.w, @l.rect.h+1)
		@m.image=@m.image.zoom_to(@m.rect.w+1, @m.rect.h+1)
		@r.image=@r.image.zoom_to(@r.rect.w, @r.rect.h+1)
		@bl.image=@bl.image.zoom_to(*@bl.rect.size)
		@b.image=@b.image.zoom_to(@b.rect.w+1, @b.rect.h)
		@br.image=@br.image.zoom_to(*@br.rect.size)
	
		if sides.include?(:top) or sides.include?(:left)
			add_child(@tl, [x0,y0])
		end
		if sides.include?(:top) or sides.include?(:left) or sides.include?(:right)
			add_child(@t, [x1, y0])
		end
		if sides.include?(:top) or sides.include(:right)
			add_child(@tr,[x2, y0])
		end
		if sides.include?(:left) or sides.include?(:top) or sides.include?(:bottom)
			add_child(@l, [x0, y1])
		end

		add_child(@m, [x1, y1])
		add_child(center_widget, [x1, y1])
		
		if sides.include?(:right) or sides.include?(:top) or sides.include?(:bottom)
			add_child(@r, [x2, y1])
		end
		if sides.include?(:bottom) or sides.include?(:left)
			add_child(@bl, [x0, y2])
		end
		if sides.include?(:bottom) or sides.include?(:left) or sides.include?(:right)
			add_child(@b, [x1, y2])
		end
		if sides.include?(:bottom) or sides.include?(:right)
			add_child(@br, [x2, y2])
		end
		
		#calc size based on which sides are present
		width=0
		if sides.size>1
			@rect.w=x3
			@rect.h=y3
		elsif sides.include?(:bottom)
			@rect.w=x3
			@rect.h=y3-y1+y0
		elsif sides.include?(:top)
			@rect.w=x3
			@rect.h=y2
		elsif sides.include?(:left)
			@rect.w=x2
			@rect.h=y3
		elsif sides.include?(:right)
			@rect.w=x3-x1+x0
			@rect.h=y3
		end
	end
end
# Index panel holds a collection of children and displayed one at a time. change_index changes the currently visible widget
class IndexPanel < Panel
	alias panel_add_child add_child
	def initialize(callbacks={})
		@selected_index=0
		super([0,0])
	end
	# Add a widget to this panel.
	def add_child(widget)
		panel_add_child(widget, [0,0])
		puts "index panel added #{widget.class.to_s} at [#{@rect.x}, #{@rect.y}]"
		#@selected_index=@children.size-1
		#resize this widget
		@rect.w=[@rect.w,widget.rect.w].max
		@rect.h=[@rect.h,widget.rect.h].max
	end
	def draw(screen)
		if not @children.empty?
			@children[@selected_index].draw(screen)
		end
	end
	def set_index(i)
		@selected_index=i
	end
	def [](i)
		@children[i]
	end
	#Filter events to child at selected index
	def receive_event(event)
		if not @children.empty?
			if @children[@selected_index].responds_to_event?(event)
				@children[@selected_index].receive_event(event)
			end
		end
	end
end
# TabPanel contains a horizontal panel of buttons styled as tabs and an index panel to show only one of the tab contents at a time
class TabPanel <Panel
	
	class Tab<IndexPanel
	
		class UnselectedTab < Button
			def initialize(widget)
				super(widget, {}, ThemeManager.instance.default_theme.tab, Frame.new(widget, ThemeManager.instance.default_theme.tab, sides=[:top]))
			end
		end
		class SelectedTab < Button
			def initialize(widget)
				super(widget, {}, ThemeManager.instance.default_theme.selectedtab, Frame.new(widget, ThemeManager.instance.default_theme.selectedtab, sides=[:top]))
				@widget=widget
			end
			
			#overload methods so that widget is not moved twice. It is the same widget.
			def set_position(position)
				dx=position[0]-@rect.x
				dy=position[1]-@rect.y
				super
				#save widget position
				widget_position=@widget.rect.topleft
				@frame.move([dx,dy])
				#restore widget position
				@widget.rect.topleft=widget_position
			end
			def move(direction)
				x=@rect.x + direction[0]
				y=@rect.y + direction[1]
				@rect.topleft=[x,y]
				#save widget position
				widget_position=@widget.rect.topleft
				@frame.move(direction)
				#restore widget position
				@widget.rect.topleft=widget_position
			end
		end
		
		def initialize(widget, index, tabpanel)
			super()
			index_panel=self
			unselectedtab=UnselectedTab.new(widget.clone)
			selectedtab=SelectedTab.new(widget.clone)
			unselectedtab.on_click.add_handler do|*args|
				tabpanel.select(index)
				puts "unselected tab clicked"
			end
			
			selectedtab.on_click.add_handler do|*args|
				tabpanel.select(index)
				puts "selected tab clicked"
			end
			
			add_child(unselectedtab)
			add_child(selectedtab)
		end
		def select
			set_index(1)
		end
		def deselect
			set_index(0)
		end
	end
	
	alias panel_add_child add_child
	def initialize(callbacks={})
		super([0,0])
		@tab_panel=HorizontalPanel.new
		@index_panel=IndexPanel.new
		panel_add_child(@tab_panel, [0,0])
		panel_add_child(@index_panel, [0,@tab_panel.rect.h])
		@selected_index=nil
	end
	def add_tab(tab_widget, panel_widget)
		if @selected_index.nil?
			@selected_index=0
		else
			@selected_index+=1
		end
		puts "added tab"
		#link the tab with the correct index
		#size=@tab_panel.children.size
		index_panel=@index_panel
		tab_panel=@tab_panel

		@tab_panel.add_child(Tab.new(tab_widget, @selected_index, self))
		@index_panel.add_child(panel_widget)
		
		#reposition index_panel
		@index_panel.set_position([@rect.x, @rect.y+@tab_panel.rect.h])
		
		#resize this widget
		@rect.w=[@tab_panel.rect.w, @index_panel.rect.w].max
		@rect.h=@tab_panel.rect.w + @index_panel.rect.w
	end
	def select(index)
		if not @selected_index.nil?
			@tab_panel[@selected_index].deselect
		end
		@selected_index=index
		@tab_panel[@selected_index].select
		@index_panel.set_index(@selected_index)
	end
end
# Radio group is a collection of buttons and labels layed out horizontally
class RadioGroup < VerticalPanel
	
	class RadioButton < Widget
		include EventSink
		def initialize(radiogroup, index, theme=ThemeManager.instance.default_theme.radiobutton)
			unselected_image=theme.unselected
			selected_image=theme.selected
			@index_panel=IndexPanel.new
			index_panel=@index_panel
			index_panel.add_child(unselected_image)
			index_panel.add_child(selected_image)
			super(@index_panel.rect.size)
			on_mouse_up.add_handler do
				index_panel.set_index(1)
				puts "radio button selected"
				radiogroup.select(index)
			end
		end
		def deselect
			@index_panel.set_index(0)
		end
		def set_position(position)
			dx=position[0]-@rect.x
			dy=position[1]-@rect.y
			super
			@index_panel.move([dx,dy])
		end
		def move(direction)
			x=@rect.x + direction[0]
			y=@rect.y + direction[1]
			@rect.topleft=[x,y]
			@index_panel.move(direction)
		end
		def draw(screen)
			@index_panel.draw(screen)
		end
	end
	
	#alias panel_add_child add_child
	def initialize
		super
		@selected_index=nil
		@radio_buttons=[]
		#instatiate all event delegates
		[:on_selection_changed].each do |symbol|
			delegate = Delegate.new
			#define events as methods
			meta_def(symbol){delegate}
		end
	end
	def add_option(widget, theme=ThemeManager.instance.default_theme.radiobutton)
		#create horizontal panel
		horizontal_panel=HorizontalPanel.new
		#create radio button
		radiobutton=RadioButton.new(self, @children.size, theme)
		@radio_buttons.push(radiobutton)
		#add button to horizontal panel
		horizontal_panel.add_child(radiobutton)
		#add widget to horizontal panel
		horizontal_panel.add_child(widget)
		#add the horizontal panel to the verticalpanel
		add_child(horizontal_panel)
		#puts "radiogroup size: [#{@rect.w},#{@rect.h}]"
	end
	def select(index)
		if not @selected_index.nil?
			@radio_buttons[@selected_index].deselect
		end
		@selected_index=index
		on_selection_changed.call(index)
	end
	def get_selected
		@selected_index
	end
end
# Checkbox is a composite of a button and a label. The button image toggles when clicked. The checkbox can be queried is_checked?
class CheckBox < HorizontalPanel
	#alias panel_add_child add_child
	def initialize(widget, theme=ThemeManager.instance.default_theme.checkbox)
		super()
		@checked=false
		checked=@checked
		@unchecked_image=theme.unchecked
		@checked_image=theme.checked
		@index_panel=IndexPanel.new
		index_panel=@index_panel
		@index_panel.add_child(@unchecked_image)
		@index_panel.add_child(@checked_image)
		checkbox=self
		@unchecked_image.on_mouse_up.add_handler do|*args|
			index_panel.set_index(1)
			checked=true
			puts "check box checked: #{checked}"
			on_selection_changed.call(checked)
		end
		
		@checked_image.on_mouse_up.add_handler do|*args|
			index_panel.set_index(0)
			checked=false
			puts "check box checked: #{checked}"
			on_selection_changed.call(checked)
		end
		add_child(index_panel)
		add_child(widget)
		
		#instatiate all event delegates
		[:on_selection_changed].each do |symbol|
			delegate = Delegate.new
			#define events as methods
			meta_def(symbol){delegate}
		end
		
	end
	def checked?
		@checked
	end
end

#allows use to drag a widget along a horizontal path and capture the position [0,1] of that widget
class HorizontalSlider < Panel
	def initialize(width=200, theme=ThemeManager.instance.default_theme.horizontalslider)
		slider=theme.slider
		@slider=slider
		@background=theme.background
		#scale background to width
		@background.image.zoom_to(width, @background.rect.h)
		super([@background.rect.w,@slider.rect.h])
		panel=self
		#allow slider to be dragged
		@slider.on_mouse_motion.add_handler do |event|
			#check for drag
			if not event.buttons.empty?
				#move the slider in the direction of the drag in the x dimension
				slider.move([event.rel[0],0])
				#keep the slider inside the bounds of the parent
				if slider.rect.centerx > panel.rect.right
					slider.rect.centerx=panel.rect.right
				elsif slider.rect.centerx < panel.rect.left
					slider.rect.centerx=panel.rect.left
				end
				panel.on_slider_changed.call(panel.position)
			end
		end
		
		add_child(@background)
		#position background centered under the slider
		@background.move([0,@slider.rect.h/2])
		add_child(@slider)
		#instatiate all event delegates
		[:on_slider_changed].each do |symbol|
			delegate = Delegate.new
			#define events as methods
			meta_def(symbol){delegate}
		end
		puts "HorizontalSlider rect: [#{@rect.w},#{@rect.h}]"
	end
	def position
		(0.0+ @slider.rect.centerx-@background.rect.left)/@background.rect.w
	end
	def postion=(pos)
		
	end
end
#TODO implement this class
#allows use to drag a widget along a vertical path and capture the position [0,1] of that widget
class VerticalSlider < Panel
	def initialize(height=200, theme=ThemeManager.instance.default_theme.verticalslider)
		slider=theme.slider
		@slider=slider
		@background=theme.background
		#scale background to width
		@background.image.zoom_to(@background.rect.h, height)
		super([@slider.rect.w, @background.rect.h])
		panel=self
		#allow slider to be dragged
		@slider.on_mouse_motion.add_handler do |event|
			#check for drag
			if not event.buttons.empty?
				#move the slider in the direction of the drag in the y dimension
				slider.move([0, event.rel[1]])
				#keep the slider inside the bounds of the parent
				if slider.rect.centery > panel.rect.bottom
					slider.rect.centery = panel.rect.bottom
				elsif slider.rect.centery < panel.rect.top
					slider.rect.centery = panel.rect.top
				end
				panel.on_slider_changed.call(panel.position)
			end
		end
		
		add_child(@background)
		#position background centered under the slider
		@background.move([@slider.rect.w/2,0])
		add_child(@slider)
		#instatiate all event delegates
		[:on_slider_changed].each do |symbol|
			delegate = Delegate.new
			#define events as methods
			meta_def(symbol){delegate}
		end
		puts "VerticalSlider rect: [#{@rect.w},#{@rect.h}]"
	end
	def position
		(0.0+ @slider.rect.centery-@background.rect.top)/@background.rect.h
	end
	def postion=(pos)
		
	end
end
#TODO implement this class
class ProgressBar
	
end
#TODO implement this class
class ScrollPanel< Panel
	def initialize(widget, size, callbacks=nil)
		super(size)
		@widget=widget
		add_child(@widget)
	end
	def set_scroll_position(position)
		@widget.set_position([@rect.x,@rect.top+position*(@rect.h-@widget.rect.h)])
		puts "set scroll position: #{@widget.rect.top}"
	end
	def draw(screen)
		#save current clip mask
		old_clip=screen.clip
		#apply the clip mask of this widget
		screen.clip=@rect
		#draw the widget as clipped
		@widget.draw(screen)
		#restore the old clip mask
		screen.clip=old_clip
	end
end
class ScrollPanelwSlider < HorizontalPanel
	def initialize(widget,size, callbacks=nil)
		super()
		scrollpanel=ScrollPanel.new(widget, size, callbacks)
		@scrollpanel=scrollpanel
		@slider=VerticalSlider.new(size[1])
		@slider.on_slider_changed.add_handler do |position|
			scrollpanel.set_scroll_position(position)
		end
		add_child(@scrollpanel)
		add_child(@slider)
	end
end

class Cursor < Widget
	# Create and set up a new Cursor object
	def initialize(theme=ThemeManager.instance.default_theme.cursor)
		super([0,0])
		@image = theme.cursor.image
		@clicking = false		# whether the cursor is clicking
		@mpos = [0,0]			# mouse curson position
		
		@rect=@image.make_rect
		
		on_mouse_motion.add_handler {|event| @mpos = event.pos}

		on_mouse_down.add_handler {|event| 
			@clicking = true
			# use a smaller rect to check if we collided with the target
			puts "click at #{event.pos[0]} #{event.pos[1]}"
		}

		on_mouse_up.add_handler {|event| @clicking = false}
	end

	# Update the cursor position
	def update
		# move the rect to the remembered position
		@rect.topleft = @mpos
		# apply offset (right and down) if we are punching
		if @clicking
			@rect.move!(2,2)
		end
	end

	def is_clicking?
		@clicking
	end
end

class ClickEvent < Rubygame::Event
	attr_reader :button, :pos
	def initialize(pos, button)
		@button=button
		@pos=pos
	end
end

# Window contains the cursor and all GUI components.
class Window
	def initialize(size, cursor=Cursor.new, root_panel=Panel.new(size, [0,0,0,127]))
		@size=size
		@cursor=cursor
		@root_panel=root_panel
		@clicking_widget=nil
		@focused_widget=nil
	end
	def get_root_panel
		@root_panel
	end
	def get_cursor
		@cursor
	end
	def get_focused_widget
		@focused_widget
	end
	def set_focused_widget(widget)
		puts "Window, focused widget changed to #{widget.class.to_s}"
		@focused_widget=widget
	end
	def process_event(event)
		case event
			when Rubygame::MouseMotionEvent
				@cursor.on_mouse_motion.call(event)
				if @cursor.is_clicking?
					@focused_widget.receive_event(event)
				end
			when Rubygame::MouseDownEvent
				@cursor.on_mouse_down.call(event)
			when Rubygame::MouseUpEvent
				@cursor.on_mouse_up.call(event)
				if @focused_widget.responds_to_event? event
					@focused_widget.receive_event(ClickEvent.new(event.pos, event.button))
				end
			when Rubygame::KeyDownEvent
				@focused_widget.receive_event(event)
			end
		#filter event down
		if @root_panel.responds_to_event?(event)
			@root_panel.receive_event(event)
		end
	end
	def draw(screen)
		@root_panel.draw(screen)
		@cursor.draw(screen)
	end
	def update()
		@root_panel.update
		@cursor.update
	end
end

class DynamicObject
	#These are needed for magical dynamic method binding
	def metaclass; class << self; self; end; end
   	def meta_eval &blk
		metaclass.instance_eval &blk; 
	end
	def meta_def (name, &blk)
		meta_eval { define_method(name, &blk) }
   	end
end


#should use Rubygame::MediaBag
#usage ThemeManager.themename.widgetname.imagename
#ex: ThemeManager.sample_theme.frame.topleft->Surface

require 'pathname'

class Theme < DynamicObject
	attr_reader :name
	def initialize(path)
		@widgets={}
		parent_path = Pathname.new(path).dirname
		file = File.new(path)
		doc = REXML::Document.new file
		# fetch the name of the theme
		@name=theme_name=doc.root.attributes["name"]
		#create theme
		doc.elements.each("theme/widget") do |element|
			puts "found #{element.name}"
			widget=DynamicObject.new
			#add the name method to the widget
			widget.meta_def(:name){element.attributes['name']}
			#add the child elements to the widget
			element.each_element() do|child|
				case child.name
					when "image"
						#puts "image found path: #{child.attributes['path']}"
						image=Image.new(parent_path+child.attributes['path'])
						widget.meta_def(:"#{child.attributes['name']}"){image.clone}
					when "color"
						#puts "color found: #{child.attributes['color']}"
						color=child.attributes['color'].split(',')
						r=color[0].to_i
						g=color[1].to_i
						b=color[2].to_i
						widget.meta_def(:"color"){[r,g,b]}
					when "font"
						#puts "font found: #{child.attributes['path']}"
						font=Rubygame::TTF.new(parent_path+child.attributes['path'],child.attributes['size'].to_i)
						widget.meta_def(:"font"){font}
					when "padding"
						#puts "padding found: #{child.attributes['padding']}"
						padding=child.attributes['padding'].to_i
						widget.meta_def(:"padding"){padding}
				end
			end
			# add the widget as a method name
			meta_def(:"#{element.attributes['name']}"){widget}
			puts "Added #{element.attributes['name']} to Theme"
		end
	end
	def method_missing(symbol, *args)
		puts "Theme#method_missing #{symbol.to_s}"
		if @widgets.has_key?(symbol.to_s)
			attr_reader symbol
			@widgets[symbol.to_s]
		else
			nil
		end
	end
end

require 'singleton'
require "rexml/document"
class ThemeManager
	include Singleton
	attr_reader :default_theme
	def initialize
		@themes={}
		@defalt_theme={}
	end
	def load(path)
		theme=Theme.new(path)
		ThemeManager.send(:define_method, :"#{theme.name}") do
			@themes[theme.name]=theme
		end
	end
	def set_default_theme(name)
		@default_theme=@themes[name]
	end
end



